/**
 * 将扁平列表转换为树形结构数据
 * @param {Array} list - 扁平列表，每个元素包含 id 和 pid 属性
 * @param {number|string} rootValue - 根节点的 pid 值
 * @return {Array} - 树形结构的数据
 */
export const transListToTreeData = (list, rootValue) => {
  const itemMap = {}
  const result = []
  list.forEach((item) => {
    if (item.id && item.pid) {
      itemMap[item.id] = item
      item.children = []
    }
  })
  const findChildren = (parentId) => {
    return list
      .filter((item) => item.pid === parentId)
      .map((child) => {
        const grandChildren = findChildren(child.id)
        if (grandChildren.length > 0) {
          child.children = grandChildren
        }
        return child
      })
  }

  result.push(...list.filter((item) => item.pid === rootValue).map((item) => findChildren(item.id)))
  return result
}

/**
 * 格式化时间戳（毫秒级）为人类可读的字符串。
 * @param {number|string} time - 时间戳（毫秒级）或可解析为毫秒级时间戳的字符串。
 * @param {boolean} [includeTime=false] - 在返回的日期字符串中是否包含时间信息（默认不包含）。
 * @returns {string} - 格式化后的时间字符串。
 * @throws {Error} - 如果输入的字符串无法解析为有效的时间戳。
 */
export const formatTime = (time, includeTime = false) => {
  let timestamp = typeof time === 'string' ? new Date(time).getTime() : time
  if (isNaN(timestamp)) {
    throw new Error('无效日期字符串: ' + time)
  }
  const now = Date.now()
  const diff = (now - timestamp) / 1000
  if (diff < 60) return '刚刚'
  if (diff < 3600) return `${Math.ceil(diff / 60)}分钟前`
  if (diff < 86400) return `${Math.floor(diff / 3600)}小时前`
  const date = new Date(timestamp)
  return includeTime
    ? `${date.getFullYear()}年${padZero(date.getMonth() + 1)}月${padZero(date.getDate())}日 ${padZero(date.getHours())}:${padZero(date.getMinutes())}`
    : `${date.getFullYear()}年${padZero(date.getMonth() + 1)}月${padZero(date.getDate())}日`
}
const padZero = (num) => {
  return num.toString().padStart(2, '0')
}
/**
 * 防抖
 * @param {Function} func 执行函数
 * @param {number} wait 等待时间
 * @param {boolean} immediate 是否立即执行
 * @return {*}
 */
export function debounce(func, wait, immediate) {
  let timeout, args, context, timestamp, result
  const later = function () {
    const last = +new Date() - timestamp
    if (last < wait && last > 0) {
      timeout = setTimeout(later, wait - last)
    } else {
      timeout = null
      if (!immediate) {
        result = func.apply(context, args)
        if (!timeout) context = args = null
      }
    }
  }
  return function (...args) {
    context = this
    timestamp = +new Date()
    const callNow = immediate && !timeout
    if (!timeout) timeout = setTimeout(later, wait)
    if (callNow) {
      result = func.apply(context, args)
      context = args = null
    }
    return result
  }
}
/**
 * 简单的深拷贝函数
 * @param {Object|Array} source 要克隆的源对象或数组
 * @returns {Object|Array} 源的深拷贝
 */
export function deepClone(source) {
  if (source === null || typeof source !== 'object') {
    throw new Error('必须是对象或数组')
  }
  if (source instanceof Date) {
    return new Date(source.getTime())
  }
  if (source instanceof RegExp) {
    let flags = ''
    if (source.flags !== undefined) {
      flags = source.flags
    }
    return new RegExp(source.source, flags)
  }
  const target = source.constructor === Array ? [] : {}
  const map = new WeakMap()
  map.set(source, target)
  Object.keys(source).forEach((key) => {
    const value = source[key]
    if (value && typeof value === 'object') {
      if (map.has(value)) {
        target[key] = map.get(value)
      } else {
        target[key] = deepClone(value)
        map.set(value, target[key])
      }
    } else {
      target[key] = value
    }
  })
  return target
}

/**
 * 判断是否是移动设备
 **/
export function isMobileDevice() {
  const userAgent = navigator.userAgent || navigator.vendor || window.opera
  const mobileRegex = /android|iphone|ipad|ipod|blackberry|iemobile|opera mini/i
  return mobileRegex.test(userAgent)
}

/**
 * 根据当前时间，返回相应的格式化时间字符串
 * @param {number|string} time
 * @return {string} 格式化后的时间字符串
 *  **/
export function formatTimeH(time) {
  if (typeof time === 'string') {
    const date = new Date(time)
    if (!isNaN(date.getTime())) {
      time = date.getTime()
    } else {
      return 'Invalid date string: ' + time
    }
  } else if (('' + time).length === 10) {
    time = parseInt(time) * 1000
  } else {
    time = +time
  }
  const d = new Date(time)
  const now = Date.now()
  const diff = (now - d) / 1000 // 转换为秒 一天为86400秒
  if (diff < 60) {
    return '刚刚'
  } else if (diff < 3600) {
    return Math.ceil(diff / 60) + '分钟前'
  } else if (diff < 86400) {
    return Math.floor(diff / 3600) + '小时前'
  } else if (diff < 365 * 86400) {
    if (diff < 7 * 86400) {
      return Math.floor(diff / 86400) + '天前'
    } else {
      return (
        (d.getMonth() + 1).toString().padStart(2, '0') +
        '月' +
        d.getDate().toString().padStart(2, '0') +
        '日'
      )
    }
  } else {
    return (
      d.getFullYear() +
      '年' +
      (d.getMonth() + 1).toString().padStart(2, '0') +
      '月' +
      d.getDate().toString().padStart(2, '0') +
      '日'
    )
  }
}

/**
 * 复制文本到剪贴板
 * @param {string} text 要复制的文本
 * **/
export function copyTextToClipboard(text) {
  if (!text) return
  if (navigator.clipboard && navigator.clipboard.writeText) {
    navigator.clipboard.writeText(text)
  } else {
    const textArea = document.createElement('textarea')
    textArea.value = text
    textArea.style.position = 'fixed'
    textArea.style.opacity = 0
    document.body.appendChild(textArea)
    textArea.focus()
  }
}

/**
 * 获取指定日期是当年的第几天
 * @param {Date|string} dateInput 日期对象或字符串
 * @return {number} 返回该日期是当年的第几天
 *  **/
export function getDayOfYear(dateInput) {
  // 如果传入的是字符串，将其转换为 Date 对象
  const date = typeof dateInput === 'string' ? new Date(dateInput) : dateInput
  // 检查日期是否有效
  if (!(date instanceof Date) || isNaN(date)) {
    throw new Error('Invalid date object')
  }
  const startOfYear = new Date(date.getFullYear(), 0, 0) // 上一年的最后一天
  const timeDiff = date - startOfYear // 毫秒差
  const millisecondsInDay = 1000 * 60 * 60 * 24 // 一天的毫秒数

  return Math.floor(timeDiff / millisecondsInDay)
}

/**
 * 从对象中挑选指定的属性，返回一个新的对象
 * @param {Object} obj 源对象
 * @param {...string} props 需要挑选的属性名
 * @return {Object} 返回一个新对象，包含指定的属性
 * @example
 * const obj = { name: 'Alice', age: 25, gender: 'female' };
 * const result = pick(obj, 'name', 'age'); // { name: 'Alice', age: 25 }
 */
export function pick(obj, ...props) {
  if (!obj || typeof obj !== 'object') {
    return {}
  }

  const propSet = new Set(props) // 将 props 转换为 Set
  return Object.fromEntries(Object.entries(obj).filter(([key]) => propSet.has(key)))
}

/**
 * 生成一个随机的十六进制颜色值
 * @return {string} 返回一个随机的颜色值，格式为 #RRGGBB
 */
export function getRandomColor() {
  const randomValues = new Uint8Array(3) // 生成 3 个随机字节
  if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
    crypto.getRandomValues(randomValues) // 使用 crypto API 生成随机数
  } else {
    for (let i = 0; i < 3; i++) {
      randomValues[i] = Math.floor(Math.random() * 256)
    }
  }
  const color = `#${Array.from(randomValues)
    .map((value) => value.toString(16).padStart(2, '0'))
    .join('')}`
  return color
}

/**
 * 去除字符串中的 HTML 标签，返回纯文本内容
 * @param {string} fragment 包含 HTML 标签的字符串
 * @return {string} 去除标签后的纯文本内容
 * @throws {Error} 如果传入的参数不是字符串，抛出错误
 */
export function removeTag(fragment) {
  if (typeof fragment !== 'string') {
    throw new Error('Input must be a string')
  }
  if (typeof DOMParser !== 'undefined') {
    try {
      return new DOMParser().parseFromString(fragment, 'text/html').body.textContent || ''
    } catch (e) {
      console.warn('DOMParser failed, falling back to regex')
    }
  }
  return fragment.replace(/<[^>]+>/g, '')
}
